/*******************************************************

        pm32stub.c
        This code is used to enter the long mode.
        we will set the GDT and Page about the long mode.
        copyright (C) 2014 -     BrooksGe
        
********************************************************/


//typedef
typedef unsigned long long UINT64;
typedef unsigned int UINT32;
typedef unsigned short UINT16;
typedef unsigned char UINT8;

//Macro definition
#define CONSTRUCT_PAGE_ATTR(AVL, G, PAT, D, A, PCD, PWT, U_S, R_W, P)\
                           (UINT16)((((AVL) & 0x07) << 9) |\
                           (((G) & 0x01) << 8) |\
                           (((PAT) & 0x01) << 7) |\
                           (((D) & 0x01) << 6) |\
                           (((A) & 0x01) << 5) |\
                           (((PCD) & 0x01) << 4) |\
                           (((PWT) & 0x01) << 3) |\
                           (((U_S) & 0x01) << 2) |\
                           (((R_W) & 0x01) << 1) |\
                           (((P) & 0x01)))

#define PAGE_TABLE_ADDR         0x300000


//Function prototype
void Construct_page(void * BeginAddr);
void Construct_page_entry(void * EnteryAddr, UINT64 BaseAddr, UINT16 attr);
void debug_print8(UINT8 row, UINT8 col, UINT8 byte);
void debug_print32(UINT8 row, UINT8 col, UINT32 dword);
void enable_longMode(void * Pml4Addr, void * entry, UINT32 selector);
void load_gdtr(void * addr);
void store_gdtr(void * sAddr);
void MemCopy(void * Dest, void * Src, UINT64 Length);



//
//This function must be placed in the first beacause of 
//some reason...
//
void MainEntry(void * file, int lenght, void * destArea, void * entry)
{
    
        MemCopy(destArea, file, lenght);
        Construct_page((void *)PAGE_TABLE_ADDR);
        enable_longMode((void *)PAGE_TABLE_ADDR, entry, 0x20);
        while(1);
        
}

//
//Function used to construct the page Hiearchary.
//      addr: the addr of pml4 talbe. the following table will place increase.
//
//      Note: the page size if 1GB. This is just used to experiment. So not complete.
//
void Construct_page(void * BeginAddr)
{
        UINT32 i;
        UINT64 * PTable;
        UINT64 Entry_Base;
        
        //PML4
        PTable = (UINT64 *)BeginAddr;
        Entry_Base = (UINT32)BeginAddr + 0x1000;
        for(i = 0; i < 512; i++, Entry_Base += 0x1000)
        {
                Construct_page_entry((void *)&(PTable[i]), 
                                     Entry_Base, 
                                     CONSTRUCT_PAGE_ATTR(0, 0, 0, 0, 0, 0, 0, 0, 1, 1));
        }
        
        //PDP
        PTable = (UINT64 *)((UINT32)BeginAddr + 0x1000);
        Entry_Base = 0;
        for(i = 0; i < 512 * 512; i++, Entry_Base += 1024 * 1024 * 1024)
        {
                Construct_page_entry((void *)&PTable[i], 
                                     Entry_Base, 
                                     CONSTRUCT_PAGE_ATTR(0, 0, 1, 0, 0, 0, 0, 0, 1, 1));
        }
}

//Construct the entry of the page table. eg. PM4LE PDPE PDTE PTE
void Construct_page_entry(void * EnteryAddr, UINT64 BaseAddr, UINT16 attr)
{
        UINT64 Value = 0;
        Value = BaseAddr | attr;
        *(UINT64 * )EnteryAddr = Value;
}


void MemCopy(void * Dest, void * Src, UINT64 Length)
{
        UINT8 * pDest = (UINT8 *)Dest;
        UINT8 * pSrc = (UINT8 *)Src;
        UINT64 i;
        
        for(i = 0; i < Length; i++)
        {
                pDest[i] = pSrc[i];            
        }
}

///////////////////////////////////////////////////////
//      debug utility
///////////////////////////////////////////////////////
typedef struct
{
       UINT8 charCode;
       UINT8 color;
}PRINT_UNIT;
typedef PRINT_UNIT (* SHOW_ARRAY)[80];

void debug_print8(UINT8 row, UINT8 col, UINT8 byte)
{
        SHOW_ARRAY p = (SHOW_ARRAY)0xb8000;
        UINT8 showcode;
        
        if((byte >> 4) >= 0 && (byte >> 4) <= 9)
                showcode = (byte >> 4) | 0x30;
        else
                showcode = (byte >> 4) + 55;
        p[row][col].charCode = showcode;
        
        if((byte & 0x0f) >= 0 && (byte & 0x0f) <= 9)
                showcode = (byte & 0x0f) | 0x30;
        else
                showcode = (byte & 0x0f) + 55;
        p[row][col + 1].charCode = showcode;
        
}

void debug_print32(UINT8 row, UINT8 col, UINT32 dword)
{
        debug_print8(row, col, dword >> 24);
        debug_print8(row, col + 2, dword >> 16);
        debug_print8(row, col + 4, dword >> 8);
        debug_print8(row, col + 6, dword);
}